#include <cgf/entityfactory.hpp>
#include <cgf/componenttemplate.hpp>
#include <cgf/componentfactory.hpp>
#include <cgf/error.hpp>

#include <boost/lexical_cast.hpp>
#include <tinyxml/tinyxml.h>
using namespace std;
using namespace CGF;



typedef ManagedContainer<Entity, std::list, boost::shared_ptr> entList;

/**
*	A creator function for the entitylist attribute.	*/
template<class t_owner>
boost::shared_ptr<Attribute> attEntitylistCreator ( std::string value, boost::shared_ptr<t_owner> owner )
{
	
	boost::shared_ptr<Attribute> att ( new Attribute_T<boost::shared_ptr<entList>, t_owner> ( entList::newInstance() , owner ) );
	
	boost::shared_ptr<entList> lst = ((Attribute_T<boost::shared_ptr<entList>, t_owner>*)att.get())->value();
	std::vector<std::string> tokens;
	boost::split ( tokens, value, boost::is_any_of ( ";, " ) );
	for ( std::vector<std::string>::iterator str = tokens.begin(); str != tokens.end(); str++ )
	{
		lst->push_back ( owner->componentFactory().lock()->entityFactory().lock()->createEntity ( (*str), owner->componentFactory().lock() ) );
	}
	return att;
}


//	Create entities based on name
boost::shared_ptr<Entity> EntityFactory::createEntity ( string name, boost::shared_ptr<ComponentFactory> fact )
{
	
	fact->entityFactory() = _this();
	
	//	Create the entity
	boost::shared_ptr<Entity> ret ( new Entity () );
	ret->setThis ( ret );
	
	//	Give it an unique ID
	ret->setAttribute<uint64_t> ( "id", produceID() );
	
	//	Store the these pointers for future reference
	ret->m_factory = _this();
	ret->m_componentFactory = fact;
	
	//	Add components if needed
	if ( name != "" )
	{
	
		if ( m_templates.count ( name ) == 0 )
			throw Error ( "Entity template " + name + " is not defined." ); 

		ret->setName( name );
		
		//	Add all the components
		boost::unordered_set<string>::iterator comp = m_templates[name]->m_componentNames.begin();
		for ( ; comp != m_templates[name]->m_componentNames.end(); comp++ )
			fact->createComponent ( *comp, ret );
		
		//	Copy all the classes
		boost::unordered_set<string>::iterator cls = m_templates[name]->m_classes.begin();
		for ( ; cls != m_templates[name]->m_classes.end(); cls++ )
			ret->addClass (*cls);
		
		//	Create the attributes
		boost::unordered_map<string, boost::shared_ptr<Attribute> >::iterator att = m_templates[name]->m_attributes.begin();
		for ( ; att != m_templates[name]->m_attributes.end(); att++ )
		{
			TemplateAttribute* tmp = (TemplateAttribute*)((*att).second.get());
		
			//	Add the attribute to the component
			ret->addAttribute ( (*att).first, attributeFactory()->createAttribute ( tmp->type, tmp->value, ret ));
		}
		
	}
	
	m_created->push_back ( ret );
	
	return ret;
}

typedef ManagedContainer<Entity, std::list, boost::shared_ptr> t_entContainer;
typedef boost::shared_ptr< t_entContainer > ptr_entContainer;


//	Load template definitions from a file
void EntityFactory::loadTemplates ( string filename, boost::shared_ptr<ComponentFactory> fact )
{
	TiXmlNode* rootnode= NULL, *classnode = NULL;
	string name, base, cname, type, value;
	
	//	Create an xml file reader
	TiXmlDocument file ( filename );
	if ( !file.LoadFile () )
		throw Error ( "Could not load the entity template file " + filename + "." );
	
	rootnode = file.FirstChildElement ( "templates" );
	rootnode = rootnode->FirstChildElement ( "entity" );
	
	//	Go through all the templates
	while ( rootnode )
	{
		try
		{
			name = GetAttribute ( rootnode, "name" );
		}
		catch ( string& s ) { throw Error ( s ); }
		
		//	Create the new template
		boost::shared_ptr<EntityTemplate> made ( new EntityTemplate() );
		made->setThis ( made );
		made->setName ( name );
		m_templates[name] = made;
		
		//	Read any possible extra class specifications
		classnode = rootnode->FirstChildElement ( "class" );
		while ( classnode )
		{
			try { cname = GetAttribute ( classnode, "name" ); }
			catch ( string& s ) { throw Error ( s ); }
			m_templates[name]->addClass ( cname );
			
			classnode = classnode->NextSibling ( "class" );
		}
		
		//	Read the components this entity is composed of
		classnode = rootnode->FirstChildElement ( "component" );
		while ( classnode )
		{
			try { cname = GetAttribute ( classnode, "name" ); }
			catch ( string& s ) { throw Error ( s ); }
			m_templates[name]->m_componentNames.insert ( cname );
			
			//	Verify the component
			if ( !fact->hasTemplate ( cname ) )
				throw Error ( "Component template " + cname + " is not defined." ); 
			
			classnode = classnode->NextSibling ( "component" );
		}
		
		//	Read any possible extra class specifications
		classnode = rootnode->FirstChildElement ( "attribute" );
		while ( classnode )
		{
			try
			{
				type = GetAttribute ( classnode, "type" );
				cname = GetAttribute ( classnode, "name" );
				value = GetAttribute ( classnode, "value" );
			}
			catch ( string& s ) { throw Error ( s ); }
			
			boost::shared_ptr<Attribute> created ( new TemplateAttribute() );
			((TemplateAttribute*)created.get())->type = type;
			((TemplateAttribute*)created.get())->value = value;
			
			m_templates[name]->m_attributes[cname] = created;
			
			classnode = classnode->NextSibling ( "attribute" );
		}
		
		rootnode = rootnode->NextSibling( "entity" );
	}
}



EntityFactory::EntityFactory(  )
{
	m_attributeFactory = boost::shared_ptr<AttributeFactory<Entity> > ( new AttributeFactory<Entity> );
	m_attributeFactory->registerCreator ( "entitylist", boost::bind ( &attEntitylistCreator<Entity>, _1, _2 ) );
	m_created = boost::shared_ptr<ManagedContainer<Entity> > ( new ManagedContainer<Entity> () );
	m_created->setThis ( m_created );
	
	m_nextID = 0;
}
